package ch5.CyclicBarrierCase;

import util.Debug;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 使用士兵打靶的例子来演示CyclicBarrier
 * 一排士兵同时开始射击，只有所有士兵都射击完毕后，才能成排整队离开射击区域
 * 换另外一排的士兵进行下一轮射击
 */

public class ShootPractice {
    // 参与打靶的全部士兵
    final Soldier[][] rank;
    // 靶子的个数，也是每排中士兵的个数
    final int N;
    // 打靶持续时间
    final int lasting;
    // 标识是否继续打靶
    volatile boolean done = false;

    // 标识下一次打靶的是哪一排的士兵
    volatile int nextLine = 0;

    final CyclicBarrier shiftBarrier;
    final CyclicBarrier startBarrier;

    public ShootPractice(int N, final int lineCount, int lasting) {
        this.N = N;
        this.lasting = lasting;
        this.rank = new Soldier[lineCount][N];
        for (int i = 0; i < lineCount; i++) {
            for (int j = 0; j < N; j++) {
                rank[i][j] = new Soldier(i * N + j);
            }
        }

        // 第二个参数是BarrierAction，最后一个线程调用了shiftBarrier.await()后，就会执行该barrierAction
        shiftBarrier = new CyclicBarrier(N, new Runnable() {
            @Override
            public void run() {
                // 更新下一轮打靶的排
                nextLine = (nextLine + 1) % lineCount;
                Debug.info("Next turn is :%d", nextLine);
            }
        });

        // N个士兵互相等待
        startBarrier = new CyclicBarrier(N);
    }

    public void start() throws InterruptedException {
        // 创建并启动工作者线程
        Thread[] threads = new Thread[N];
        for (int i = 0; i < N; i++) {
            threads[i] = new Shooting(i);
            threads[i].start();
        }

        // 指定时间后停止打靶
        Thread.sleep(lasting * 1000);
        stop();
        for (Thread t : threads) {
            t.join();
        }
        Debug.info("Practice finished.");
    }

    public void stop() {
        done = true;
    }

    class Shooting extends Thread {
        final int index;

        public Shooting(int index) {
            this.index = index;
        }

        @Override
        public void run() {
           Soldier soldier;
           try {
               while (!done) {
                   soldier = rank[nextLine][index];
                   // 一排中的士兵必须同时开始射击
                   startBarrier.await();
                   soldier.fire();
                   // 一排中的士兵必须等待该排中的所有士兵全都射击完毕才能换下一排
                   shiftBarrier.await();
               }
           } catch (InterruptedException e) {
               e.printStackTrace();
           } catch (BrokenBarrierException e) {
               e.printStackTrace();
           }
        }
    }


    static class Soldier {
        private final int seqNo;

        public Soldier(int seqNo) {
            this.seqNo = seqNo;
        }

        public void fire() {
            System.out.println("soldier seqNo: " + seqNo + " is firing...");
        }
    }


    public static void main(String[] args) throws InterruptedException {
        ShootPractice sp = new ShootPractice(4, 5, 5);
        sp.start();
    }
}
